package rcs34.android.libs.HSVOPicker;

import java.util.ArrayList;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.util.AttributeSet;
import android.util.FloatMath;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;

/**
 * This class displays a colored slider, which can be used to pick up a color.
 * It can interpolate between two or more colors.
 * 
 * @author kocian
 * 
 */

public class ColorPicker extends View implements OnTouchListener {
  private Paint paint = new Paint();
  // Init colors - only for Graphical Layout design
  private int[] colors = new int[] { Color.BLACK, Color.WHITE };
  private int gapsCount = 1;
  private float colorDistance=1.0f;
  private int absoluteSliderPosition = 0;
  private float relativeSliderPosition = 0.0f;
  private int height;
  private int width;
  private int heightQuater;
  private int heightHalf;
  private int sliderWidth;
  private boolean dirty;
  private ArrayList<Listener> listeners = new ArrayList<Listener>();
  private ColorChangeEvent changeHolder;;
  
  
  /**
   * Event which represents change in a ColorPicker status
   * 
   * @author kocian
   * 
   */
  public class ColorChangeEvent {
    private int color;
    private ColorPicker source;
    
    /**
     * Get the new selected color
     * 
     * @return Color integer
     */
    public int getColor() {
      return color;
    }
    
    /**
     * Return source of the change
     * 
     * @return HSVPicker which fired the change
     */
    public ColorPicker getSource() {
      return source;
    }
  }
  /**
   * ColorPicker listener interface
   * 
   * @author kocian
   * 
   */
  public interface Listener {
    public void colorChanged(ColorChangeEvent _change);
  }
  
  /**
   * Add a Listener, which will be informed about changes in HSV values
   * 
   * @param _listener
   *          The listener
   */
  public void addListener(Listener _listener) {
    listeners.add(_listener);
  }
  
  /**
   * Unregister an explicit Listener
   * 
   * @param _listener
   *          The listener to remove
   */
  public void removeListener(Listener _listener) {
    listeners.remove(_listener);
  }
  
  /**
   * Unregister all Listeners
   */
  public void removeAllListeners() {
    listeners.clear();
  }
  
  private void fireChange(){
    changeHolder.color = getSelectedColor();
    for (Listener l : listeners) {
      l.colorChanged(changeHolder);
    }
  }
  /**
   * Constructor
   * @param context
   */
  public ColorPicker(Context context) {
    super(context);
    init();
  }
  /**
   * Constructor
   * @param context
   */
  public ColorPicker(Context _context, AttributeSet _attrs, int _defStyle) {
    super(_context, _attrs, _defStyle);
    init();
  }
  /**
   * Constructor
   * @param context
   */
  public ColorPicker(Context _context, AttributeSet _attrs) {
    super(_context, _attrs);
    init();
  }
  /**
   * Common constructor stuff
   */
  private void init(){
    changeHolder=new ColorChangeEvent();
    changeHolder.source=this;
    setOnTouchListener(this);
  }
  @Override
  protected void onSizeChanged(int _w, int _h, int _ow, int _oh) {
    width = _w;
    height = _h;
    heightHalf = height / 2;
    heightQuater = height / 4;
    sliderWidth = width - height;
  }
  /**
   * Move slider to an absolute position. It fires change to listeners
   * @param _x
   */
  private void moveSlider(int _x) {
    invalidate(absoluteSliderPosition, 0, absoluteSliderPosition + height,
        height);
    absoluteSliderPosition = Math.max(Math.min(_x, sliderWidth), 0);
    invalidate(absoluteSliderPosition, 0, absoluteSliderPosition + height,
        height);
  }
  
  /**
   * Select colors to interpolate (select) between.
   * 
   * @param _colors
   *          Array of colors
   */
  public void setColors(int[] _colors) {
    colors = _colors;
    gapsCount = colors.length - 1;
    colorDistance = 1.0f / gapsCount; 
    }
  
  /**
   * Move a slider to an absolute position.
   * 
   * @param _x
   */
  public void moveToAbsolute(int _x) {
    int abs = _x - heightHalf;
    relativeSliderPosition = (float) abs / sliderWidth;
    moveSlider(abs);
    fireChange();
  }
  
  /**
   * Move to position relative to the width. It can be used to recover setting
   * (saved
   * with getRelative());
   * 
   * @param _position
   *          Relative position of the slider
   */
  public void moveToRelative(float _position) {
    relativeSliderPosition = Math.max(Math.min(_position,1),0);
    if (width > 0) {
      moveSlider(Math.round(sliderWidth * _position));
    } else {
      dirty = true;
    }
    fireChange();
  }
  
  public void step (int _direction){
    float step=_direction*0.01f;
    moveToRelative(relativeSliderPosition+step);
  }
  
  /**
   * Get relative position of the slider. The position can be used for recover
   * the actual setting lately.
   * 
   * @return Relative position of the slider
   */
  public float getRelative() {
    return relativeSliderPosition;
  }
  
  /**
   * Get color on a relative position.
   * 
   * @param _position
   *          The relative position
   * @return Color number
   */
  private int getColor(float _position) {
    int result = 0;
    if (_position <= 0) {
      result= colors[0];
    }else if (_position >= 1){
      result= colors[gapsCount];
    }else{
    float fromIndexFloat=_position/colorDistance;
    int fromIndex = (int)FloatMath.floor(fromIndexFloat);
    int from = colors[fromIndex];   
    int to = colors[fromIndex + 1];
    float interpolation = fromIndexFloat - fromIndex;
    for (int i = 0; i < 32; i += 8) {
      int fromPart = from >> i & 0xFF;
      int toPart = to >> i & 0xFF;
      if (fromPart != toPart) {
        int value = Math.round(fromPart + (toPart - fromPart) * interpolation);
        result += value << i;
      } else {
        result += fromPart << i;
      }
    }
    }
    return result;
  }
  
  /**
   * Get a color at an actual slider position
   * 
   * @return Selected color
   */
  public int getSelectedColor() {
    return getColor(relativeSliderPosition);
  }
  
  @Override
  public void onDraw(Canvas _canvas) {
    // Maybe we have set relative position prior to layout, so let's finish our
    // work here
    if (dirty) {
      dirty = false;
      moveToRelative(relativeSliderPosition);
    }
    paint.setStyle(Style.FILL);
    for (int i = heightHalf; i < width - heightHalf; i++) {
      int abs = i - heightHalf;
      paint.setColor(getColor((float) abs / sliderWidth));
      _canvas.drawLine(i, heightQuater, i, heightHalf + heightQuater, paint);
    }
    paint.setStyle(Style.FILL);
    paint.setColor(getSelectedColor());
    _canvas.drawCircle(absoluteSliderPosition + heightHalf, heightHalf,
        heightHalf, paint);
    paint.setStyle(Style.STROKE);
    paint.setColor(Color.WHITE);
    _canvas.drawCircle(absoluteSliderPosition + heightHalf, heightHalf,
        heightHalf, paint);
  }
  
  public boolean onTouch(View _v, MotionEvent _event) {
    moveToAbsolute((int) _event.getX());
    return true;
  }

  
}
